// @copyright 2017-2018 zzu_softboy <zzu_softboy@163.com>
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Created by softboy on 2018/01/18.

#include "gtest/gtest.h"

#include "pdk/stdext/preprocessor/Array.h"
#include "pdk/stdext/preprocessor/facilities/IsEmpty.h"
#include "pdk/stdext/preprocessor/list/At.h"
#include "pdk/stdext/preprocessor/list/Size.h"
#include "pdk/stdext/preprocessor/seq/Element.h"
#include "pdk/stdext/preprocessor/seq/Size.h"
#include "pdk/stdext/preprocessor/tuple/Element.h"
#include "pdk/stdext/preprocessor/tuple/Size.h"
#if PDK_PP_VARIADICS
#include "pdk/stdext/preprocessor/variadic/Size.h"
#include "pdk/stdext/preprocessor/variadic/Element.h"
#endif

#define ARRAY_EMPTY (0, ())
#define ARRAY_ONE (1, ())
#define ARRAY (3, (0, 1, 2))
#define ARRAY_LARGE (33, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32))
#define ARRAY_VERY_LARGE (64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63))

TEST(ArrayTest, testElementAccess)
{
   ASSERT_EQ(PDK_PP_IS_EMPTY(PDK_PP_ARRAY_ELEM(0, ARRAY_ONE)), 1);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(1, ARRAY), 1);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(2, (5, (0, 1, 2, 3, 4))), 2);
   ASSERT_EQ(28, PDK_PP_ARRAY_ELEM(28, ARRAY_LARGE));
   ASSERT_EQ(17, PDK_PP_ARRAY_ELEM(17, (33, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32))));
   ASSERT_EQ(42, PDK_PP_ARRAY_ELEM(42, ARRAY_VERY_LARGE));
   ASSERT_EQ(62, PDK_PP_ARRAY_ELEM(62, (64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63))));
}

TEST(ArrayTest, testArraySize)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(ARRAY), 3);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE((5, (0, 1, 2, 3, 4))), 5);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(ARRAY_LARGE), 33);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE((33, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32))), 33);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(ARRAY_VERY_LARGE), 64);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE((64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63))), 64);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(ARRAY_EMPTY), 0);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(ARRAY_ONE), 1);
}

TEST(ArrayTest, testVariadicElement)
{
# if PDK_PP_VARIADICS
   ASSERT_EQ(PDK_PP_VARIADIC_ELEM(2, PDK_PP_ARRAY_ENUM(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_VARIADIC_ELEM(3, PDK_PP_ARRAY_ENUM((5, (0, 1, 2, 3, 4)))), 3);
   ASSERT_EQ(PDK_PP_VARIADIC_ELEM(31, PDK_PP_ARRAY_ENUM(ARRAY_LARGE)), 31);
   ASSERT_EQ(PDK_PP_VARIADIC_ELEM(13, PDK_PP_ARRAY_ENUM((33, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)))), 13);
   ASSERT_EQ(PDK_PP_VARIADIC_ELEM(39, PDK_PP_ARRAY_ENUM(ARRAY_VERY_LARGE)), 39);
   ASSERT_EQ(PDK_PP_VARIADIC_ELEM(24, PDK_PP_ARRAY_ENUM((64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63)))), 24);
   ASSERT_EQ(PDK_PP_VARIADIC_SIZE(PDK_PP_ARRAY_ENUM((5, (0, 1, 2, 3, 4)))), 5);
   ASSERT_EQ(PDK_PP_VARIADIC_SIZE(PDK_PP_ARRAY_ENUM(ARRAY_LARGE)), 33);
   ASSERT_EQ(PDK_PP_VARIADIC_SIZE(PDK_PP_ARRAY_ENUM(ARRAY_VERY_LARGE)), 64);
   ASSERT_EQ(PDK_PP_VARIADIC_SIZE(PDK_PP_ARRAY_ENUM((64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63)))), 64);
   ASSERT_EQ(PDK_PP_VARIADIC_SIZE(PDK_PP_ARRAY_ENUM(ARRAY_ONE)), 1);
#endif
}

TEST(ArrayTest, testToList)
{
   ASSERT_EQ(PDK_PP_LIST_AT(PDK_PP_ARRAY_TO_LIST(ARRAY), 1), 1);
   ASSERT_EQ(PDK_PP_LIST_AT(PDK_PP_ARRAY_TO_LIST((5, (0, 1, 2, 3, 4))), 4), 4);
   ASSERT_EQ(PDK_PP_LIST_AT(PDK_PP_ARRAY_TO_LIST((33, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32))), 26), 26);
   ASSERT_EQ(PDK_PP_LIST_AT(PDK_PP_ARRAY_TO_LIST(ARRAY_VERY_LARGE), 60), 60);
   ASSERT_EQ(PDK_PP_LIST_SIZE(PDK_PP_ARRAY_TO_LIST(ARRAY_EMPTY)), 0);
   ASSERT_EQ(PDK_PP_LIST_SIZE(PDK_PP_ARRAY_TO_LIST(ARRAY_ONE)), 1);
}

TEST(ArrayTest, testToSeq)
{
   ASSERT_EQ(PDK_PP_IS_EMPTY(PDK_PP_SEQ_ELEM(0, PDK_PP_ARRAY_TO_SEQ(ARRAY_ONE))), 1);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(2, PDK_PP_ARRAY_TO_TUPLE(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(1, PDK_PP_ARRAY_TO_TUPLE((5, (0, 1, 2, 3, 4)))), 1);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(26, PDK_PP_ARRAY_TO_TUPLE(ARRAY_LARGE)), 26);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(37, PDK_PP_ARRAY_TO_TUPLE((64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63)))), 37);
}

TEST(ArrayTest, testToTuple)
{
#if PDK_PP_VARIADICS
   ASSERT_EQ(PDK_PP_IS_EMPTY(PDK_PP_TUPLE_ELEM(0, PDK_PP_ARRAY_TO_TUPLE(ARRAY_ONE))), 1);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(2, PDK_PP_ARRAY_TO_TUPLE(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(1, PDK_PP_ARRAY_TO_TUPLE((5, (0, 1, 2, 3, 4)))), 1);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(26, PDK_PP_ARRAY_TO_TUPLE(ARRAY_LARGE)), 26);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(37, PDK_PP_ARRAY_TO_TUPLE((64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63)))), 37);
#else
   ASSERT_EQ(PDK_PP_IS_EMPTY(PDK_PP_TUPLE_ELEM(1, 0, PDK_PP_ARRAY_TO_TUPLE(ARRAY_ONE))), 1);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(3, 2, PDK_PP_ARRAY_TO_TUPLE(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(5, 1, PDK_PP_ARRAY_TO_TUPLE((5, (0, 1, 2, 3, 4)))), 1);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(33, 26, PDK_PP_ARRAY_TO_TUPLE(ARRAY_LARGE)), 26);
   ASSERT_EQ(PDK_PP_TUPLE_ELEM(64, 37, PDK_PP_ARRAY_TO_TUPLE((64, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63)))), 37);
#endif
}

TEST(ArrayTest, testInsert)
{
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_INSERT(ARRAY_ONE, 0, 63)), 63);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_INSERT(ARRAY, 2, 40)), 0);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(1, PDK_PP_ARRAY_INSERT(ARRAY, 1, 40)), 40);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(2, PDK_PP_ARRAY_INSERT(ARRAY, 1, 40)), 1);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_INSERT(ARRAY, 1, 40)), 4);
   
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(8, PDK_PP_ARRAY_INSERT(ARRAY_LARGE, 22, 1000)), 8);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(22, PDK_PP_ARRAY_INSERT(ARRAY_LARGE, 22, 1000)), 1000);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(26, PDK_PP_ARRAY_INSERT(ARRAY_LARGE, 22, 1000)), 25);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_INSERT(ARRAY_LARGE, 22, 1000)), 34);
   
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_INSERT(ARRAY_EMPTY, 0, 25)), 25);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_INSERT(ARRAY_EMPTY, 0, 1000)), 1);
}


TEST(ArrayTest, testPopBack)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_POP_BACK(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_POP_BACK(ARRAY_LARGE)), 32);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_POP_BACK(ARRAY_VERY_LARGE)), 63);
}

TEST(ArrayTest, testPopFront)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_POP_FRONT(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_POP_FRONT(ARRAY_LARGE)), 32);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_POP_FRONT(ARRAY_VERY_LARGE)), 63);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(1, PDK_PP_ARRAY_POP_FRONT(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(31, PDK_PP_ARRAY_POP_FRONT(ARRAY_LARGE)), 32);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(55, PDK_PP_ARRAY_POP_FRONT(ARRAY_VERY_LARGE)), 56);
}

TEST(ArrayTest, testPushFront)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_BACK(ARRAY, 3)), 4);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_BACK(ARRAY_LARGE, 33)), 34);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_BACK(ARRAY_EMPTY, 10)), 1);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_BACK(ARRAY_ONE, 44)), 2);
   
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_PUSH_BACK(ARRAY, 3)), 0);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(33, PDK_PP_ARRAY_PUSH_BACK(ARRAY_LARGE, 33)), 33);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_PUSH_BACK(ARRAY_EMPTY, 136)), 136);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(1, PDK_PP_ARRAY_PUSH_BACK(ARRAY_ONE, 245)), 245);
}

TEST(ArrayTest, testPushBack)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_FRONT(ARRAY, 555)), 4);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_FRONT(ARRAY_LARGE, 666)), 34);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_FRONT(ARRAY_EMPTY, 10)), 1);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_PUSH_FRONT(ARRAY_ONE, 131)), 2);
   
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_PUSH_FRONT(ARRAY, 555)), 555);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(33, PDK_PP_ARRAY_PUSH_FRONT(ARRAY_LARGE, 33)), 32);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_PUSH_FRONT(ARRAY_EMPTY, 136)), 136);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_PUSH_FRONT(ARRAY_ONE, 56)), 56);
}

TEST(ArrayTest, testRemove)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REMOVE(ARRAY_ONE, 0)), 0);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REMOVE(ARRAY, 1)), 2);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REMOVE(ARRAY_LARGE, 17)), 32);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REMOVE(ARRAY_VERY_LARGE, 27)), 63);
   
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_REMOVE(ARRAY, 2)), 0);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(29, PDK_PP_ARRAY_REMOVE(ARRAY_LARGE, 2)), 30);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(62, PDK_PP_ARRAY_REMOVE(ARRAY_VERY_LARGE, 48)), 63);
}

TEST(ArrayTest, testReplace)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REPLACE(ARRAY_ONE, 0, 3)), 1);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REPLACE(ARRAY_VERY_LARGE, 27, 1000)), 64);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_REPLACE(ARRAY_ONE, 0, 69)), 69);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_REPLACE(ARRAY, 1, 44)), 0);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(29, PDK_PP_ARRAY_REPLACE(ARRAY_LARGE, 29, 999)), 999);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(38, PDK_PP_ARRAY_REPLACE(ARRAY_VERY_LARGE, 37, 1)), 38);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(28, PDK_PP_ARRAY_REPLACE(ARRAY_VERY_LARGE, 28, 1)), 1);
}

TEST(ArrayTest, testReverse)
{
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REVERSE(ARRAY_VERY_LARGE)), 64);
   ASSERT_EQ(PDK_PP_IS_EMPTY(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_REVERSE(ARRAY_ONE))), 1);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(0, PDK_PP_ARRAY_REVERSE(ARRAY)), 2);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(29, PDK_PP_ARRAY_REVERSE(ARRAY_LARGE)), 3);
   ASSERT_EQ(PDK_PP_ARRAY_ELEM(38, PDK_PP_ARRAY_REVERSE(ARRAY_VERY_LARGE)), 25);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REVERSE(ARRAY_EMPTY)), 0);
   ASSERT_EQ(PDK_PP_ARRAY_SIZE(PDK_PP_ARRAY_REVERSE(ARRAY_ONE)), 1);
}
